#import "BasicOpenGLView.h"
#import <GLUT/glut.h>

#ifdef __APPLE__
#define _MACOSX
#endif

int once = 0; 


static CFAbsoluteTime gStartTime = 0.0f;
// set app start time
static void setStartTime (void)
{   
  gStartTime = CFAbsoluteTimeGetCurrent ();
}

static CFAbsoluteTime getElapsedTime (void)
{   
  return CFAbsoluteTimeGetCurrent () - gStartTime;
}


// error reporting as both window message and debugger string
void reportError (char * strError)
{
    NSMutableDictionary *attribs = [NSMutableDictionary dictionary];
    [attribs setObject: [NSFont fontWithName: @"Monaco" size: 9.0f] 
      forKey: NSFontAttributeName];
    [attribs setObject: [NSColor whiteColor] 
      forKey: NSForegroundColorAttributeName];

  NSString * errString = [NSString stringWithFormat:@"Error: %s.", strError];
  NSLog (@"%@\n", errString);
}

// if error dump gl errors to debugger string, return error
GLenum glReportError (void)
{
  GLenum err = glGetError();
  if (GL_NO_ERROR != err)
    reportError ((char *) gluErrorString (err));
  return err;
}

@implementation BasicOpenGLView

// pixel format definition
+ (NSOpenGLPixelFormat*) basicPixelFormat
{
    NSOpenGLPixelFormatAttribute attributes [] = {
        NSOpenGLPFAWindow,
        NSOpenGLPFADoubleBuffer,  // double buffered
        // 16 bit depth buffer
        NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16, 
        (NSOpenGLPixelFormatAttribute)nil
    };
    return [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] 
      autorelease];
}

// handles resizing of GL need context update and if the window dimensions
// change, a a window dimension update, reseting of viewport and an update of
// the projection matrix
- (void) resizeGL
{
  NSRect rectView = [self bounds];
  // should test if it's actually changing
  preview_3D->resize(rectView.size.width ,rectView.size.height);
}

// per-window timer function, basic time based animation preformed here
- (void)animationTimer:(NSTimer *)timer
{ 
  if(preview_3D->g_AutoRotate || damage){
    damage = false;
    [self drawRect:[self bounds]];
  }
}

-(IBAction) openDocument: (id) sender
{
  NSOpenPanel *tvarNSOpenPanelObj  = [NSOpenPanel openPanel];
  // Create an array of strings specifying valid extensions and HFS file types.
  NSArray *fileTypes = [NSArray arrayWithObjects:
                        @"obj",
                        @"OBJ",
                        @"off",
                        @"OFF",
                        @"wrl",
                        @"WRL",
                        @"mesh",
                        @"MESH",
                        NSFileTypeForHFSTypeCode('TEXT'),
                        nil];
  
 
  NSInteger tvarNSInteger = [tvarNSOpenPanelObj runModalForTypes:fileTypes];
  if(tvarNSInteger == NSOKButton){
    [self load_file:[tvarNSOpenPanelObj filename]];
  }
}

- (id) openDocumentWithContentsOfFile: (NSString*)file_name 
                              display: (BOOL)display
{
  [self load_file:file_name];
  return self;
}

- (BOOL)load_file:(NSString *) file_name
{
  damage = true;
  const char * mesh_file_name = [file_name UTF8String];
  return preview_3D->load_mesh_from_file(mesh_file_name);
}

-(IBAction) saveDocumentAs: (id) sender
{
  NSSavePanel     *savePanel; 
  
  savePanel = [NSSavePanel savePanel]; 
  [savePanel setTitle:@"Save as (.obj by default)"];
  // Create an array of strings specifying valid extensions and HFS file types.
  NSArray *fileTypes = [NSArray arrayWithObjects:
                        @"obj",
                        @"OBJ",
                        @"off",
                        @"OFF",
                        NSFileTypeForHFSTypeCode('TEXT'),
                        nil];
  [savePanel setAllowedFileTypes:fileTypes]; 
  [savePanel setTreatsFilePackagesAsDirectories:NO]; 
  [savePanel setAllowsOtherFileTypes:YES];
  
  if (NSOKButton == [savePanel runModalForDirectory:NSHomeDirectory() file:@""]) 
  {
    [self save_file:[savePanel filename]];
  } 
}

- (BOOL)save_file:(NSString *) file_name
{
  damage = true;
  const char * mesh_file_name = [file_name UTF8String];
  return preview_3D->save_mesh_to_file(mesh_file_name);
}

-(void)keyDown:(NSEvent *)theEvent
{
    NSString *characters = [theEvent characters];
    if ([characters length]) {
        char character = [characters characterAtIndex:0];
      
      if(!TwKeyPressed(character, 0)){
        switch (character) {
        case 'h':
          NSLog(@"Help");
          break;
      }
    }
  }
  damage = true;
}

- (void)mouseDown:(NSEvent *)theEvent
{
  NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  NSRect rectView = [self bounds];
  location.y = rectView.size.height - location.y;
  
  if(!preview_3D->mouse_down(location.x,location.y)){
  }
  damage = true;
}

- (void)rightMouseDown:(NSEvent *)theEvent
{
  if(!TwMouseButton(TW_MOUSE_PRESSED,TW_MOUSE_RIGHT)){

  }
  damage = true;
}

- (void)otherMouseDown:(NSEvent *)theEvent
{
  damage = true;
}

- (void)mouseUp:(NSEvent *)theEvent
{
  NSPoint location = [self convertPoint:[theEvent locationInWindow] 
                               fromView:nil];
  NSRect rectView = [self bounds];
  location.y = rectView.size.height - location.y;
  if(!preview_3D->mouse_up(location.x,location.y)){
  } 
  damage = true;
}

- (void)rightMouseUp:(NSEvent *)theEvent
{  
  if(!TwMouseButton(TW_MOUSE_RELEASED,TW_MOUSE_RIGHT)){
  }
  damage = true;
}

- (void)otherMouseUp:(NSEvent *)theEvent
{
  [self mouseUp:theEvent];
  
  damage = true;
}

-(void)viewDidMoveToWindow {
  [[self window] setAcceptsMouseMovedEvents:YES];
  [[self window] makeFirstResponder:self];
}

- (void)mouseMoved:(NSEvent *)theEvent
{
  NSPoint location = [self convertPoint:[theEvent locationInWindow] 
    fromView:nil];
  NSRect rectView = [self bounds];
  location.y = rectView.size.height - location.y;
  if(!preview_3D->mouse_move(location.x, location.y))
  {
  }
  damage = true;
}

- (void)mouseDragged:(NSEvent *)theEvent
{
  
  NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  NSRect rectView = [self bounds];
  location.y = rectView.size.height - location.y;
  if(!preview_3D->mouse_move(location.x, location.y))
  {
  }
  damage = true;
}

- (void)scrollWheel:(NSEvent *)theEvent
{

  NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  NSRect rectView = [self bounds];
  location.y = rectView.size.height - location.y;
  if(!preview_3D->mouse_scroll(location.x, location.y,[theEvent deltaY]))
  {
  }
  damage = true;
}

- (void)rightMouseDragged:(NSEvent *)theEvent
{
  [self mouseDragged: theEvent];
}

- (void)otherMouseDragged:(NSEvent *)theEvent
{
  [self mouseDragged: theEvent];
}

- (void) drawRect:(NSRect)rect
{    
  // setup viewport and prespective
  // forces projection matrix update (does test for size changes)
  [self resizeGL]; 
  // Draw tweak bars
  preview_3D->draw((int)(getElapsedTime()*1000));
  glReportError ();
  
  glFlush();
  [[self openGLContext] flushBuffer];
}

// ---------------------------------

// set initial OpenGL state (current context is set)
// called after context is created
- (void) prepareOpenGL
{
  const GLint swapInt = 1;

  // set to vbl sync
  [[self openGLContext] setValues:&swapInt 
    forParameter:NSOpenGLCPSwapInterval];
  
  // init GL stuff here
  //  glEnable(GL_DEPTH_TEST);
  //glShadeModel(GL_SMOOTH);    
  //glEnable(GL_CULL_FACE);  
  if(once < 1){
    
    preview_3D = new Preview3D((int)(getElapsedTime()*1000));
//    preview_3D->load_mesh_from_file(
//      "/Users/ajx/Documents/Preview3D/data/cow.off");
    once++;
  }
    
}

// this can be a troublesome call to do anything heavyweight, as it is called
// on window moves, resizes, and display config changes.  So be careful of
// doing too much here.  window resizes, moves and display changes (resize,
// depth and display config change)
- (void) update 
{
  [super update];  
}

-(id) initWithFrame: (NSRect) frameRect
{
  NSOpenGLPixelFormat * pf = [BasicOpenGLView basicPixelFormat];

  self = [super initWithFrame: frameRect pixelFormat: pf];
    return self;
}

// ---------------------------------

- (BOOL)acceptsFirstResponder
{
  return YES;
}

// ---------------------------------

- (BOOL)becomeFirstResponder
{
  return  YES;
}

// ---------------------------------

- (BOOL)resignFirstResponder
{
  return YES;
}

// ---------------------------------

- (void) awakeFromNib
{
  setStartTime ();
  // start animation timer
  timer = [NSTimer timerWithTimeInterval:(1.0f/60.0f) target:self 
    selector:@selector(animationTimer:) userInfo:nil repeats:YES];
  [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
  // ensure timer fires during resize
  [[NSRunLoop currentRunLoop] addTimer:timer 
    forMode:NSEventTrackingRunLoopMode]; 
}


@end
